#include "BitArray.h"
#include "global.h"

BitArray::BitArray (int size, bool value)
    : QBitArray(size, value), valid(true), mResizeEnable(true)
{
    lEndian = true;
    isUnsigned = true;
}

BitArray::BitArray(const QBitArray& other)
    : QBitArray(other), valid(true)
{
    lEndian = true;
    isUnsigned = true;
}


BitArray::~BitArray()
{

}

QString BitArray::toBinString(bool sep)
{
    if(!valid)
        return QString("Invalid!");

    QString str;

    for(int i = this->size()-1; i >= 0; i--)
    {//qDebug("a");
        if(this->testBit(i))
            str.append("1");
        else
            str.append("0");
        if(lEndian)
        {
            if(sep && i > 0 && i%4 == 0)
                str.append(",");
        }
        else
        {
            if(sep && i > 0 && i%4 == this->size()%4)
                str.append(",");
        }
    }


    return str;
}

QString binStrToHexStr(QString binStr)
{
    QString hexStr = "*", fill("0000");
    if(binStr.size() <= 4)
    {
        binStr.insert(0, fill.left(4-binStr.size()));
        if(binStr == "0000") hexStr = "0";
        if(binStr == "0001") hexStr = "1";
        if(binStr == "0010") hexStr = "2";
        if(binStr == "0011") hexStr = "3";
        if(binStr == "0100") hexStr = "4";
        if(binStr == "0101") hexStr = "5";
        if(binStr == "0110") hexStr = "6";
        if(binStr == "0111") hexStr = "7";
        if(binStr == "1000") hexStr = "8";
        if(binStr == "1001") hexStr = "9";
        if(binStr == "1010") hexStr = "a";
        if(binStr == "1011") hexStr = "b";
        if(binStr == "1100") hexStr = "c";
        if(binStr == "1101") hexStr = "d";
        if(binStr == "1110") hexStr = "e";
        if(binStr == "1111") hexStr = "f";
    }
    return hexStr;
}

QString hexStrToBinStr(QString hexStr)
{
    QString binStr = "";
    if(hexStr.size() == 1)
    {
        if(hexStr == "0") binStr = "0000";
        if(hexStr == "1") binStr = "0001";
        if(hexStr == "2") binStr = "0010";
        if(hexStr == "3") binStr = "0011";
        if(hexStr == "4") binStr = "0100";
        if(hexStr == "5") binStr = "0101";
        if(hexStr == "6") binStr = "0110";
        if(hexStr == "7") binStr = "0111";
        if(hexStr == "8") binStr = "1000";
        if(hexStr == "9") binStr = "1001";
        if(hexStr == "a") binStr = "1010";
        if(hexStr == "b") binStr = "1011";
        if(hexStr == "c") binStr = "1100";
        if(hexStr == "d") binStr = "1101";
        if(hexStr == "e") binStr = "1110";
        if(hexStr == "f") binStr = "1111";
    }
    return binStr;
}

QString hexStrToAsciiStr(QString hexStr)
{
    QString asciiStr;
    if(hexStr.size() <= 2)
    {
        bool ok;
        int tmp = hexStr.toInt(&ok, 16);
        if(ok)
        {
            char c = (char)tmp;
            if(!QChar(c).isPrint())
                asciiStr = ".";
            else
                asciiStr = c;
        }
    }
    return asciiStr;
}

QString asciiStrToHexStr(QString asciiStr)
{
    QString hexStr = "*";
    if(hexStr.size() == 1)
    {
        int tmp;
        QChar c = asciiStr.at(0);
        if(c.isUpper())
        {
            tmp = c.toTitleCase().toAscii();
        }
        else
        {
            tmp = c.toAscii();
        }
        hexStr = QString::number(tmp, 16);
    }
    return hexStr;
}

QString BitArray::toHexString(bool sep)
{
    if(!valid)
        return QString("Invalid!");

    if(this->size() == 0)
        return QString();

    QString binStr, hexStr;
    binStr = this->toBinString();
    if(binStr.length() < 4 || binStr.length() % 4 != 0)
        binStr.insert(0, QString("0000").mid(0, 4-binStr.length()%4));
    for(int i = binStr.size()-4; i >= 0; i-=4)
    {
        if(i >= 0)
            hexStr.insert(0, binStrToHexStr(binStr.mid(i, 4)));
    }

    return hexStr;
}

bool BitArray::fromBinString(QString binStr)
{
    int strIdx = 0;
    this->fill(false);
    if(binStr.size() <= MAX_ARRAY_SIZE)
    {
        if(this->resize(binStr.size()))
            strIdx = this->size()-1;
        else
            strIdx = binStr.size()-1;
    }
    else
    {
        if(this->resize(MAX_ARRAY_SIZE))
            strIdx = this->size()-1;
        else
            strIdx = MAX_ARRAY_SIZE-1;
    }

    for(int i = 0; i < this->size() && strIdx >= 0; i++)
    {
        if(binStr[strIdx] == '0')
            this->setBit(i, false);
        else if(binStr[strIdx] == '1')
            this->setBit(i, true);
        else
            return false;
        strIdx--;
    }


    return true;
}

bool BitArray::fromHexString(QString hexStr)
{
    QString binStr;
    QString tmpStr;
    for(int i = 0; i < hexStr.size(); i++)
    {
        tmpStr = hexStrToBinStr(QString(hexStr[i]));
        if(tmpStr.isEmpty())
            return false;
        binStr.append(tmpStr);
    }
    return fromBinString(binStr);
}



bool BitArray::fromDecString(QString decStr)
{
    if(decStr.isEmpty())
    {
        this->resize(0);
        return true;
    }

    bool ok;
    if(isUnsigned)
    {
        //uint val = dezStr.toUInt(&ok, 10);
        qulonglong val = decStr.toULongLong(&ok, 10);
        if(ok)
            return fromBinString(QString::number(val, 2));
    }
    else
    {
        //int val = dezStr.toInt(&ok, 10);
        qulonglong val = decStr.toLongLong(&ok, 10);
        if(ok)
            return fromBinString(QString::number(val, 2));
    }
    fromBinString(QString::number(0, 2));
    return false;
}

QString BitArray::toDecString()
{
    if(!valid)
        return QString("Invalid!");

    if(this->size() == 0)
        return QString();

    if(this->size() > 64)
        return "Size > 64 not supported!";
    bool ok;
    QString dezStr, binStr;
    if(lEndian)
        binStr = this->toBinString();
    else
        binStr = this->byteSwap().toBinString();


    QString fillOnes("1111111111111111111111111111111111111111111111111111111111111111");
    QString fillZeros("0000000000000000000000000000000000000000000000000000000000000000");
    if(isUnsigned)
    {
        binStr.insert(0, fillZeros.left(64-binStr.size()));
        qulonglong ui = binStr.toULongLong(&ok, 2);
        dezStr = QString::number(ui);
    }
    else
    {
        if(binStr[0] == '0')
            binStr.insert(0, fillZeros.left(64-binStr.size()));
        else
            binStr.insert(0, fillOnes.left(64-binStr.size()));

        qulonglong ui = binStr.toULongLong(&ok, 2);
        qlonglong i = ui;
        dezStr = QString::number(i);
    }

    if(!ok)
        return "Conversion failed!";

    return dezStr;
}

QString BitArray::toAsciiString()
{
    if(!valid)
        return QString("Invalid!");

    QString asciiStr;
    QString hexStr = this->toHexString();
    if(hexStr.size() % 2)
        hexStr = QString("0") + hexStr;
    for(int i = 0; i < hexStr.size(); i+=2)
    {
        asciiStr += hexStrToAsciiStr(hexStr.mid(i, 2));
    }
    return asciiStr;
}


bool BitArray::fromAsciiString(QString asciiStr)
{
    QString hexStr;
    for(int i = 0; i < asciiStr.size(); i++)
        hexStr += asciiStrToHexStr(asciiStr.mid(i, 1));
    return this->fromHexString(hexStr);

}

BitArray  BitArray::operator+(const BitArray &other)
{
    BitArray ret(*this);
    if(this->size() > other.size())
        ret.fill(false, this->size());
    else
        ret.fill(false, other.size());
    bool carry = false;
    for(int i = 0; i < this->size() && i < other.size(); i++)
    {
        int sum = (int)this->at(i) + (int)other.at(i) + (int)carry;
        switch(sum)
        {
        case 0: ret.setBit(i, false); carry = false; break;
        case 1: ret.setBit(i, true); carry = false; break;
        case 2: ret.setBit(i, false); carry = true; break;
        case 3: ret.setBit(i, true); carry = true; break;
        default: break;
        }
    }
    return ret;
}

BitArray BitArray::operator-(const BitArray &other)
{
    BitArray ret(*this);
    if(this->size() > other.size())
        ret.fill(false, this->size());
    else
        ret.fill(false, other.size());
    bool carry = false;
    for(int i = 0; i < this->size() && i < other.size(); i++)
    {
        int diff = (int)this->at(i) - (int)other.at(i) - (int)carry;
        switch(diff)
        {
        case 0: ret.setBit(i, false); carry = false; break;
        case 1: ret.setBit(i, true); carry = false; break;
        case -1: ret.setBit(i, true); carry = true; break;
        case -2: ret.setBit(i, false); carry = true; break;
        default: break;
        }
    }
    return ret;
}

BitArray BitArray::operator*(const BitArray &other)
{
    BitArray ret(this->size()+other.size(), false);
    BitArray thisArr(*this);
    thisArr.resize(this->size()+other.size());

    for(int i = 0; i < other.size(); i++)
    {
        if(other.at(i))
            ret = ret + (thisArr<<i);
    }
    return ret;
}

BitArray BitArray::operator/(const BitArray &other)
{
    BitArray ret(this->size(), false);
    if(other.isZero())
    {
        ret.setValid(false);
        return ret;
    }

    BitArray one(other.size(), false);
    one.setBit(0, true);

    if(other == one)
    {
        return *this;
    }

    BitArray tmpThis(*this);
    BitArray tmpOther(other);
    BitArray tmpAdd(other);

    if(this->size() > other.size())
        tmpOther.resize(this->size());
    else if(this->size() < other.size())
        tmpThis.resize(other.size());

    tmpAdd.resize(tmpThis.size());
    one.resize(tmpThis.size());

    if(tmpOther > tmpThis)
    {
        return ret;
    }

    ret = one;
    while(tmpOther < tmpThis)
    {
        tmpOther = tmpOther << 1;
        ret = ret << 1;
    }

    if(tmpOther == tmpThis)
        return ret;

    tmpOther = tmpOther >> 1;
    ret = ret >> 1;

    qDebug(tmpThis.toBinString().toAscii().data());
    qDebug(tmpOther.toBinString().toAscii().data());

    while(tmpOther < tmpThis)
    {
        tmpOther = tmpOther + tmpAdd;
        ret = ret + one;
        //qDebug(tmpOther.toBinString().toAscii().data());
    }
    return ret;
}

BitArray BitArray::operator<<(int sl)
{
    BitArray tmp(*this);
    tmp.fill(false);

    for(int i = 0; i+sl < this->size(); i++)
    {
        tmp.setBit(i+sl, this->at(i));
    }
    return tmp;
}

BitArray BitArray::operator>>(int sr)
{
    BitArray tmp(*this);
    if(!this->isUnsigned && this->at(this->size()-1))
        tmp.fill(true);
    else
        tmp.fill(false);
    for(int i = 0; i+sr < this->size(); i++)
    {
        tmp.setBit(i, this->at(i+sr));
    }
    return tmp;
}

bool BitArray::operator>(const BitArray &other) const
{
    if(this->size() !=  other.size())
        return false;
    for(int i = this->size()-1; i > 0; i--)
    {
        if(this->at(i) && !(other.at(i)))
            return true;
        if(!(this->at(i)) && other.at(i))
            return false;
    }
    return false;
}

bool BitArray::operator<(const BitArray &other) const
{
    return !((*this) >= other);
}

bool BitArray::operator>=(const BitArray &other) const
{
    return (*this)>other || (*this)==other;
}

bool BitArray::operator<=(const BitArray &other) const
{
    return (*this)<other || (*this)==other;
}

bool BitArray::operator==(const BitArray &other) const
{
    if(this->size() !=  other.size())
        return false;
    for(int i = 0; i < this->size(); i++)
    {
        if(this->at(i) != other.at(i))
            return false;
    }
    return true;
}

bool BitArray::isZero() const
{
    for(int i = 0; i < this->size(); i++)
    {
        if(this->at(i))
            return false;
    }
    return true;
}

bool BitArray::resize(int size)
{
    if(!mResizeEnable)
        return false;
    QBitArray::resize(size);
    return true;
}

BitArray BitArray::byteSwap()
{
    int origSize = this->size();
    if(origSize%8 != 0)
        this->resize(origSize + (8-origSize%8));

    BitArray ret(*this);
    for(int i = 0; i < this->size(); i+=8)
    {
        for(int j = 0; j < 8; j++)
        {
            ret.setBit(i+j, this->at(this->size()-8-i+j));
        }
    }
    this->resize(origSize);
    ret.resize(origSize);
    return ret;
}

BitArray BitArray::bitReverse()
{
    BitArray ret(*this);
    for(int i = 0; i < this->size(); i++)
    {
        ret.setBit(i, this->at(this->size()-1-i));
    }
    return ret;
}

BitArray BitArray::rotateLeft(int rotValue)
{
    BitArray ret(*this);
    ret = (ret << rotValue) | (ret >> this->size()-rotValue);
    return ret;
}


BitArray BitArray::rotateRight(int rotValue)
{
    BitArray ret(*this);
    ret = (ret >> rotValue) | (ret << this->size()-rotValue);
    return ret;
}




